using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using ProjectEuler.Core.Helpers;

namespace ProjectEuler.Core
{
    public class Problem23 : IProjectEulerProblem
    {
        private const int SMALLEST_ABUNDANT_NUMBER = 12;
        private const int ALWAYS_ABUNDANT = 28123;

        public Problem23()
        {
            IsAbundantLookup = new bool?[ALWAYS_ABUNDANT];
        }

        protected IList<int> AllPositiveIntegersWhichCannotBeWrittenAsSumOfTwoAbundantNumbers()
        {
            var list = new List<int>();
            for(int i=1;i<= ALWAYS_ABUNDANT;i++)
            {
                if(!CanBeWrittenAsTheSumOfTwoAbundantNumbers(i))
                {
                    list.Add(i);
                }
            }
            return list;
        }

        protected bool CanBeWrittenAsTheSumOfTwoAbundantNumbers(int n)
        {
            for (int addend1 = SMALLEST_ABUNDANT_NUMBER; addend1 <= n; addend1++)
            {
                int addend2 = n - addend1;
                if (IsAbundant(addend2) && IsAbundant(addend1))
                {
                    return true;
                }
            }
            return false;
        }

        // getting the factors and summing takes a lot of time, so store results
        // in a lookup table so the calculation only needs to be made once
        private readonly bool?[] IsAbundantLookup;          // populate this lookup as we go
        protected bool IsAbundant(int n)
        {
            if(IsAbundantLookup[n] == null)
            {
                var factors = MathHelper.FactorsOf(n);
                var sumOfProperFactors = factors.Sum() - n;
                IsAbundantLookup[n] = sumOfProperFactors > n;
            }
            return IsAbundantLookup[n].Value;
        }

        public int Number
        {
            get { return 23; }
        }

        public string Description
        {
            get {
                return "Find the sum of all the positive integers which cannot be written as the sum of two abundant numbers.";
            }
        }

        public string Answer
        {
            get { return AllPositiveIntegersWhichCannotBeWrittenAsSumOfTwoAbundantNumbers().Sum().ToString(); }
        }
    }

    [TestFixture]
    public class Problem23Tests : Problem23
    {
        [Test]
        public void Twelve_should_test_positive_for_an_abundant_number()
        {
            Assert.AreEqual(true, IsAbundant(12));
        }

        [Test]
        public void Two_should_not_be_abundant()
        {
            Assert.AreEqual(false, IsAbundant(2));
        }

        [Test]
        public void Three_should_not_be_abundant()
        {
            Assert.AreEqual(false, IsAbundant(3));
        }
    }
}